package com.browsesoft.htmlcomponent;

import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;

import com.browsesoft.ExtendElement;

/**
 * 树组件
 * 
 * @author Browsesoft
 * @version 1.0
 */
public class HTMLTreeComponent extends HTMLBasicComponent {
	/**
	 * 当前选中的节点
	 */
	protected String selectedID = null;

	/**
	 * 扩展的model节点
	 */
	protected LinkedList expandedNodes = new LinkedList();

	// description元素
	protected Element descriptionElement = null;

	/**
	 * 构造器
	 * 
	 * @param parent
	 *            组件的父组件
	 * @throws Exception
	 */
	public HTMLTreeComponent(HTMLBasicComponent parent) throws Exception {
		super(parent);
	}

	/**
	 * 初始化时创建子组件并保存模板中该组件的description元素
	 * 
	 * @param request
	 * @param e
	 * @throws Exception
	 */
	public void init(HttpServletRequest request, Element e) throws Exception {
		// 创建子组件并保存组件元素
		super.init(request, e);
		// 克隆element中的description元素
		descriptionElement = (Element) e.getElementsByTagName("description")
				.item(0).cloneNode(true);
		// 得到初始展开方式
		String mode = e.getAttribute("expandmode");
		// 看用户是否设置了展开到第几级
		String lev = this.getElement().getAttribute("level");
		// 如果为全部展开
		if (mode != null && mode.equals("all")
				&& (lev == null || lev.equals(""))) {
			// 得到所有可以展开的节点放入扩展表中
			Object root = ((HTMLTreeModel) model).getRootNode();
			this.expandedNodes.addAll(this.getAllChildren(root));
		}
		// 得到名称为point的隐藏域
		LinkedList list = ExtendElement
				.getElementsByInclude(e, "name", "point");
		// 如果没有该隐藏域
		if (list.size() == 0) {
			// 抛出异常
			throw new RuntimeException("树组件必须有point隐藏域");
		}
		// 设置该隐藏域名称为point_组件名称
		Element point = (Element) list.get(0);
		point.setAttribute("name", "point_" + this.getName());
		// 如果设置了第几级
		// 因此组件会被HTMLBasicComponent和actorSelectorContainer初始化
		// 在HTMLBasicComponent初始化是model为空,所已加入限定条件model != null
		if (model != null && lev != null && !lev.equals("")) {
			// 转为int型
			int level = Integer.parseInt(lev);
			// 如果level小于等于0,返回
			if (level < 0) {
				throw new Exception("设定展开层数错误!");
			}
			// 迭代得到展开到设定的级的所有字节点
			// 的到根节点
			Object root = ((HTMLTreeModel) model).getRootNode();
			LinkedList temp = new LinkedList();
			// 装在根节点
			temp.add(root);
			// 清空装载节点expandNodes链表
			this.expandedNodes.clear();
			// 装在设定的层数显示的所有节点
			this.setLevelNodes(temp, level);
		}
	}

	/**
	 * 得到设定的级数的所有节点
	 */
	public void setLevelNodes(LinkedList list, int level) throws Exception {
		// 把链表中非叶子的节点装入expandNodes链表
		this.addNodes(list);
		// 为了得到所有需要的节点
		while ((level - 1) > 0) {
			level--;
			Iterator nodeIter = list.iterator();
			// 为了得到每个节点的子节点
			while (nodeIter.hasNext()) {
				Object o = nodeIter.next();
				// 得到子节点
				LinkedList l = ((HTMLTreeModel) this.model).getChildNodes(o);
				this.setLevelNodes(l, level);
			}
		}
	}

	protected void addNodes(LinkedList list) throws Exception {
		// 为了不把叶子装入
		Iterator iter = list.iterator();
		while (iter.hasNext()) {
			Object obj = iter.next();
			// 如果该节点不是叶子
			if (obj != null
					&& ((HTMLTreeModel) model).getChildNodes(obj).size() > 0) {
				// 装入
				if (!this.expandedNodes.contains(obj)) {
					this.expandedNodes.add(obj);
				}
			}
		}
	}

	/**
	 * 重载setModel方法，从模型中得到初始扩展结点
	 */
	public void setModel(HTMLBasicModel m) throws Exception {
		super.setModel(m);
	}

	/**
	 * 根据Model的内容更新需要处理的元素内容，在产生页面时要调用
	 * 
	 * @throws java.lang.Exception
	 */
	protected void updateElement() throws Exception {
		// 删除element的description元素
		ExtendElement.removeNodes(element, "description");
		// 模板的根元素
		Object modelRootObject = ((HTMLTreeModel) model).getRootNode();
		if (modelRootObject != null) {
			createElement(element, modelRootObject, 0);
			String isroot = this.getElement().getAttribute("isroot");
			if (isroot.equals("false")) {
				Element des = (Element) element.getElementsByTagName(
						"description").item(0);
				des.removeChild(des.getFirstChild());
			}
		}
	}

	/**
	 * 处理用户请求，将需要Model处理的内容提交给Model
	 * 
	 * @param request
	 *            客户端请求
	 * @throws java.lang.Exception
	 */
	public void service(HttpServletRequest request, HttpServletResponse response)
			throws Exception {
		super.service(request, response);
		// 从请求中得到当前选中的节点
		String id = request.getParameter("selectedid");
		// 有当前选择节点的请求
		if (id != null && !id.equals("")) {
			// 替换当前选择的节点
			selectedID = id;
		}
		if (request.getParameter("point_" + this.getName()) == null) {
			return;
		}
		String p = request.getParameter("point_" + this.getName());
		// 清空隐藏域的值
		if (p != null && !p.equals("")) {
			// 根据id 找到结点对象
			Object object = ((HTMLTreeModel) model).getObject(p);
			if (object == null || isExpanded(object)) {
				expandedNodes.remove(object);
			} else {
				expandedNodes.add(object);
			}
		}
	}

	/**
	 * 通过递归调用生成树
	 * 
	 */
	protected void createElement(Element e, Object modelNode, int level)
			throws Exception {
		// 取得element的description节点
		Element des = (Element) e.getElementsByTagName("description").item(0);
		for (int i = 0; i < level; i++) {
			// 缩进
			Element block = (Element) descriptionElement.getElementsByTagName(
					"indent").item(0);
			des.appendChild(block.cloneNode(true));
		}
		// 复制节点
		Element node = (Element) this.getTreeNode(modelNode).cloneNode(true);
		// 把节点插入根元素下
		des.appendChild(node);
		// 设置节点属性
		setNode(node, modelNode);
		// 判断节点是否扩展
		if (isExpanded(modelNode)) {
			LinkedList ls = ((HTMLTreeModel) model).getChildNodes(modelNode);
			Iterator iter = ls.iterator();
			while (iter.hasNext()) {
				Object childNode = iter.next();
				createElement(e, childNode, level + 1);
			}
		}
	}

	/**
	 * 得到要复制的节点
	 */
	protected Element getTreeNode(Object modelNode) throws Exception {
		// 如果是扩展节点
		if (isExpanded(modelNode)) {
			// 如果是选中的
			if (((HTMLTreeModel) model).getID(modelNode).equals(selectedID)) {
				return (Element) descriptionElement.getElementsByTagName(
						"curexpand").item(0);
			}
			// 否则，不是选中的
			else {
				return (Element) descriptionElement.getElementsByTagName(
						"expand").item(0);
			}
		}
		// 否则，不是叶子
		else if (((HTMLTreeModel) model).getChildNodes(modelNode).size() > 0) {
			// 如果是选中的
			if (((HTMLTreeModel) model).getID(modelNode).equals(selectedID)) {
				return (Element) descriptionElement.getElementsByTagName(
						"curunexpand").item(0);
			}
			// 否则，不是选中的
			else {
				return (Element) descriptionElement.getElementsByTagName(
						"unexpand").item(0);
			}
		}
		// 否则，是叶子
		else {
			// 如果是选中的
			if (((HTMLTreeModel) model).getID(modelNode).equals(selectedID)) {
				return (Element) descriptionElement.getElementsByTagName(
						"curleaf").item(0);
			}
			// 否则，不是选中的
			else {
				return (Element) descriptionElement
						.getElementsByTagName("leaf").item(0);
			}
		}
	}

	/**
	 * 设置树节点属性
	 * 
	 * @param node
	 *            树节点
	 * @param modelNode
	 *            对应的model
	 */
	protected void setNode(Element node, Object modelNode) throws Exception {
		// 得到树的连接
		LinkedList list = ExtendElement.findElements(node, "type", "treelink");
		if (list.size() > 0) {
			Element treeLink = (Element) list.get(0);
			// 设置树连接参数
			setTreeNode(treeLink, node, modelNode);
		}
		// 得到节点连接
		Element nodeLink = (Element) ExtendElement.findElements(node, "type",
				"nodelink").get(0);
		((HTMLTreeModel) model).setContextNode(nodeLink, modelNode);
		// 设置节点连接参数
		setContextNode(nodeLink, node, modelNode);
	}

	/**
	 * 判断是否设置了点击树上的非叶子的树打开关闭
	 * 
	 * @param nodeLink
	 *            节点元素
	 */
	private boolean isOpenOrClose(Object obj, Element nodeLink)
			throws Exception {
		// 如果是叶子,不能实现点击字体打开关闭
		if (((HTMLTreeModel) this.model).getChildNodes(obj).size() == 0) {
			return false;
		}
		// 如果不是叶子,但未设定expand="expand"标记返回false
		else if (nodeLink.getAttribute("expand") != null
				&& nodeLink.getAttribute("expand").equals("expand")) {
			return true;
		} else {
			return false;
		}
	}

	/**
	 * 设置树连接节点属性
	 * 
	 * @param treeLink
	 *            树连接节点
	 * @param node
	 *            树连接节点的父节点
	 * @param modelNode
	 *            对应model
	 */
	protected void setTreeNode(Element treeLink, Element node, Object modelNode)
			throws Exception {
		String pointID = ((HTMLTreeModel) model).getID(modelNode);
		treeLink.setAttribute("name", pointID);
		// 修改onclick属性中的point
		String onclick = treeLink.getAttribute("onclick");
		onclick = onclick.replaceFirst("point", "point_" + this.getName());
		treeLink.setAttribute("onclick", onclick);
	}

	/**
	 * 设置内容节点属性
	 * 
	 * @param nodeLink
	 *            内容节点
	 * @param node
	 *            内容节点的父亲节点
	 * @param modelNode
	 *            对应的model
	 */
	protected void setContextNode(Element nodeLink, Element node,
			Object modelNode) throws Exception {
		// 设置图标
		this.setIcon(node, modelNode);
		// 设置内容节点ID
		String link = nodeLink.getAttribute("onclick");
		// 替换ID
		String id = ((HTMLTreeModel) model).getID(modelNode);
		if (nodeLink.getAttribute("notreplacepoint").equals("")) {
			link = link.replaceAll("point=", "point=" + id);
		}
		nodeLink.setAttribute("onclick", link);
		// 如果该节点不是叶子而且设定了点击字体打开/关闭
		if (this.isOpenOrClose(modelNode, nodeLink)) {
			// 设置名称和onclic事件
			setTreeNode(nodeLink, node, modelNode);
		}
		// 设置节点文本
		Text name = this.getPage().getDocument().createTextNode(
				((HTMLTreeModel) model).getName(modelNode));
		ExtendElement.replaceElementContext(nodeLink, name);
	}

	/**
	 * 判断结点是否扩展
	 * 
	 * @param object
	 *            节点
	 * @return 是否扩展
	 */
	public boolean isExpanded(Object object) {
		for (int i = 0; i < expandedNodes.size(); i++) {
			if (object.equals(expandedNodes.get(i))) {
				return true;
			}
		}
		return false;
	}

	/**
	 * 得到某节点的所有非叶子节点,包括后代
	 */
	public Collection getAllChildren(Object node) throws Exception {
		Collection result = new LinkedList();
		// 得到所有直接子
		Iterator iter = ((HTMLTreeModel) model).getChildNodes(node).iterator();
		// 如果有子
		if (iter.hasNext()) {
			// 将该节点添入结果中
			result.add(node);
			// 对于每一个子
			while (iter.hasNext()) {
				// 得到该子所有符合条件的后代添加到结果中
				result.addAll(this.getAllChildren(iter.next()));
			}
		}
		return result;
	}

	public LinkedList getExpandNodes() {
		return this.expandedNodes;
	}

	/**
	 * 设置节点前面的图片
	 * 
	 * @param node
	 * @param modelNode
	 */
	public void setIcon(Element node, Object modelNode) {
		// 得到节点前面的图片
		NodeList nl = node.getElementsByTagName("img");
		for (int i = 0; i < nl.getLength(); i++) {
			Element image = (Element) nl.item(i);
			// 如果有icon属性
			if (image.hasAttribute("icon")) {
				// 得到src属性
				String src = image.getAttribute("src");
				src = src.replaceAll("tree_resource", ((HTMLTreeModel) model)
						.getIcon((modelNode)));
				image.setAttribute("src", src);
			}
		}
	}
}